package com.qys.livesMall.shop.scheduledTask;

/**
 * @packageName:
 * @className:TimeWheelRunner
 * @Description:
 * @author:guomx
 * @date:2023/7/718:22
 */

import cn.hutool.json.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.qys.livesMall.common.exception.QysException;
import com.qys.livesMall.common.utils.MessageUtils;
import com.qys.livesMall.open.enums.PushMessageTypeEnum;
import com.qys.livesMall.open.modal.dto.app.PushUserDeviceContentDTO;
import com.qys.livesMall.open.modal.dto.mail.PushAllDeviceDTO;
import com.qys.livesMall.open.modal.dto.mail.PushTitleDTO;
import com.qys.livesMall.open.service.PushUserDeviceService;
import com.qys.livesMall.open.uniapp.Result;
import com.qys.livesMall.shop.constants.OrderStatus;
import com.qys.livesMall.shop.constants.PayStatus;
import com.qys.livesMall.shop.constants.ShopOrderMessageConstants;
import com.qys.livesMall.shop.mapper.ShopGoodsSkuStocksMapper;
import com.qys.livesMall.shop.modal.entity.SeckillOrder;
import com.qys.livesMall.shop.modal.entity.ShopOrderIdea;
import com.qys.livesMall.shop.modal.entity.ShopOrderInfo;
import com.qys.livesMall.shop.modal.entity.ShopOrderSku;
import com.qys.livesMall.shop.service.SeckillOrderService;
import com.qys.livesMall.shop.service.ShopOrderIdeaService;
import com.qys.livesMall.shop.service.ShopOrderInfoService;
import com.qys.livesMall.shop.service.ShopOrderSkuService;
import io.netty.util.HashedWheelTimer;
import io.netty.util.Timeout;
import io.netty.util.TimerTask;
import jakarta.annotation.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;

import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Component
public class TimeWheelRunner implements ApplicationRunner {
    private static final Logger log = LoggerFactory.getLogger(TimeWheelRunner.class);
    @Resource
    private ShopOrderInfoService infoService;
    @Resource
    private ShopOrderSkuService orderSkuService;
    @Resource
    private ShopOrderIdeaService ideaService;
    @Resource
    private ShopGoodsSkuStocksMapper skuStocksMapper;
    @Resource
    private PushUserDeviceService pushUserDeviceService;
    private final HashedWheelTimer timer;
    @Resource
    private SeckillOrderService seckillOrderService;
    private final Map<String, Timeout> timeoutMap;
    public TimeWheelRunner() {
        this.timer = new HashedWheelTimer();
        this.timeoutMap = new ConcurrentHashMap<>();
    }

    @Override
    public void run(ApplicationArguments args) throws Exception {
        try {
            // 初始化和启动时间轮
            timer.start();
            log.info("Time wheel started.");
            // 初始化删除订单
            deleteOrderTask();
            log.info("Cancel order");
            // 在应用关闭时停止时间轮
            Runtime.getRuntime().addShutdownHook(new Thread(() -> {
                try {
                    timer.stop();
                    log.info("Time wheel stopped.");
                }catch (Exception e){
                    log.error("Error stopping time wheel: {}", e.getMessage());
                }

            }));

        }catch (Exception e){
            log.error("Error running time wheel: {}", e.getMessage());
        }
    }

    public void scheduleCancellation(String orderId, Duration delay) {
        TimerTask cancellationTask = timeout -> {
            try {
                cancelOrder(orderId); // 超时后执行取消订单的操作

            }catch (Exception e){
                log.error("Error executing cancelOrder task: {}", e.getMessage());
            }
        };

        Timeout timeout = timer.newTimeout(cancellationTask, delay.toMillis(), TimeUnit.MILLISECONDS);
        timeoutMap.put(orderId, timeout); // 将超时任务加入到映射中
    }
    public void scheduleSeckillCancellation( Duration delay) {
        TimerTask cancellationTask = timeout -> {
            try {
                seckill(); // 超时后执行取消订单的操作

            }catch (Exception e){
                log.error("Error executing seckill task: {}", e.getMessage());
            }
        };

        Timeout timeout = timer.newTimeout(cancellationTask, delay.toMillis(), TimeUnit.MILLISECONDS);
    }
    public void seckill(){
        try {
            PushAllDeviceDTO userDeviceDTO = new PushAllDeviceDTO();
            PushTitleDTO pushTitleDTO=new PushTitleDTO();
            pushTitleDTO.setChinese("秒杀");
            pushTitleDTO.setEnglish(ShopOrderMessageConstants.SECKILL);
            userDeviceDTO.setTitle(pushTitleDTO);
            PushUserDeviceContentDTO contentDTO = new PushUserDeviceContentDTO();
            contentDTO.setType(PushMessageTypeEnum.SECKILL_PUSH);
            JSONObject json = new JSONObject();
            json.putOnce("chinese","秒杀即将开始");
            json.putOnce("english","The Seckill event is about to begin");
            contentDTO.setData(json.toString());
            userDeviceDTO.setContent(contentDTO);
            Result pushFlag = pushUserDeviceService.pushAllUserDevice(userDeviceDTO);
        }catch (Exception e){
            log.error("Error executing seckill task: {}", e.getMessage());
        }

    }

    public void cancelOrder(String orderId) {
        ShopOrderInfo orderInfo = infoService.getById(orderId);
        if (ObjectUtils.isNotEmpty(orderInfo)){
            if (!orderInfo.getOrderStatus().equals(OrderStatus.CANCELED)&&!orderInfo.getPayStatus().equals(PayStatus.HAVE_PAYMENT)){
                LambdaQueryWrapper<ShopOrderSku> orderSkuQueryWrapper = new LambdaQueryWrapper<>();
                orderSkuQueryWrapper.eq(ShopOrderSku::getOrderId,orderInfo.getId());
                List<ShopOrderSku> orderSkus = orderSkuService.list(orderSkuQueryWrapper);
                if (ObjectUtils.isNotEmpty(orderSkus)){
                    for (ShopOrderSku orderSku : orderSkus) {
                        orderSku.setOrderStatus(OrderStatus.CANCELED);
                        orderSkuService.updateById(orderSku);
                        Long stocksId = orderSku.getStocksId();
                        Integer num = orderSku.getNum();
                        skuStocksMapper.decount(stocksId,-num);
                    }
                    orderInfo.setOrderStatus(OrderStatus.CANCELED);
                    if(infoService.updateById(orderInfo)){
                        //如果是秒杀任务
                        SeckillOrder seckillOrder = seckillOrderService.getById(orderInfo.getId());
                        if (ObjectUtils.isNotEmpty(seckillOrder)){
                            seckillOrder.setStatus(PayStatus.PAYMENT_FAILURE);
                            if(!seckillOrderService.updateById(seckillOrder)) throw new QysException(MessageUtils.message("system.error.order"));
                        }
                        timeoutMap.remove(orderId); // 从超时任务映射中移除
                        ShopOrderIdea idea = new ShopOrderIdea();
                        idea.setIdea(MessageUtils.message("order.overdue"));
                        idea.setOrderId(orderInfo.getId());
                        idea.setDescription(MessageUtils.message("order.overdue"+".2"));
                        idea.setCreateTime(System.currentTimeMillis());
                        ideaService.saveOrUpdate(idea);
                    }
                }
            }
        }
        // 执行取消订单的逻辑
        log.info("Cancel order:[{}]",orderId);
    }

    public void deleteOrderTask() {
        long halfHourAgoTimeStamp = System.currentTimeMillis() - (30 * 60 * 1000);
        LambdaQueryWrapper<ShopOrderInfo> orderInfoQueryWrapper = new LambdaQueryWrapper<>();
        orderInfoQueryWrapper.eq(ShopOrderInfo::getPayStatus, PayStatus.NONPAYMENT);
        orderInfoQueryWrapper.eq(ShopOrderInfo::getOrderStatus, OrderStatus.WAIT_PAY);
        orderInfoQueryWrapper.eq(ShopOrderInfo::getIsDelete,"0");
        orderInfoQueryWrapper.apply("create_time <= " + halfHourAgoTimeStamp);
        List<ShopOrderInfo> orderInfos=infoService.list(orderInfoQueryWrapper);
        if (ObjectUtils.isEmpty(orderInfos)){
            return;
        }
        orderInfos=orderInfos.stream().filter(orderInfo-> orderInfo.getCreateTime() <= halfHourAgoTimeStamp).collect(Collectors.toList());
        List<Long> orderInfoIds = orderInfos.stream().map(ShopOrderInfo::getId).collect(Collectors.toList());
        orderInfos=orderInfos.stream().peek(orderInfo->{
            orderInfo.setOrderStatus(OrderStatus.CANCELED);
        }).collect(Collectors.toList());
        List<ShopOrderIdea> ideas = new ArrayList<>();
        for (ShopOrderInfo orderInfo : orderInfos) {
            ShopOrderIdea idea = new ShopOrderIdea();
            idea.setIdea(MessageUtils.message("order.overdue"));
            idea.setOrderId(orderInfo.getId());
            idea.setDescription(MessageUtils.message("order.overdue"+".3"));
            idea.setCreateTime(System.currentTimeMillis());
            ideas.add(idea);
        }
        //秒杀订单
        List<SeckillOrder> seckillOrders=seckillOrderService.listByIds(orderInfoIds);
        if (CollectionUtils.isNotEmpty(seckillOrders)){
            for (SeckillOrder seckillOrder : seckillOrders) {
                seckillOrder.setStatus(PayStatus.PAYMENT_FAILURE);
            }
            seckillOrderService.updateBatchById(seckillOrders);
        }
        LambdaQueryWrapper<ShopOrderSku> orderSkuQueryWrapper = new LambdaQueryWrapper<>();
        orderSkuQueryWrapper.in(ShopOrderSku::getOrderId,orderInfoIds);
        List<ShopOrderSku> orderSkus = orderSkuService.list(orderSkuQueryWrapper);
        for (ShopOrderSku orderSku : orderSkus) {
            orderSku.setOrderStatus(OrderStatus.CANCELED);
            orderSkuService.updateById(orderSku);
            Long stocksId = orderSku.getStocksId();
            Integer num = orderSku.getNum();
            skuStocksMapper.decount(stocksId,-num);
        }
        // 批量删除订单
        if (!orderInfos.isEmpty()) {
            infoService.updateBatchById(orderInfos);
            ideaService.saveBatch(ideas);
            log.info("Cancel order size:[{}]", orderInfos.size());
        } else {
            log.info("There are no eligible orders to delete");
        }

    }
    public void cancelAllOrders() {
        timeoutMap.values().forEach(Timeout::cancel); // 取消所有的超时任务
        timeoutMap.clear(); // 清空超时任务映射
    }

}

